﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krile.Data;
using System.Threading;

namespace Krile.Forms.MainForm.Controls.Surface
{
    public partial class Surface : UserControl
    {
        Thread popThread;
        public Surface()
        {
            InitializeComponent();
            scroller.SmallChange = 10;
            compositor.OnVerticalPositionChanged += new Action(compositor_OnVerticalPositionChanged);
            compositor.OnStatusesChanged += new Action(compositor_OnStatusesChanged);
            compositor.OnSelectedItemChanged += new Action(compositor_OnSelectedItemChanged);
            popThread = new Thread(new ThreadStart(PopWatcher));
            popThread.Start();
            Core.OnExit += new Action(Core_OnExit);
        }

        void Core_OnExit()
        {
            popThread.Abort();
        }

        /// <summary>
        /// ステータス一覧
        /// </summary>
        internal XStatus[] Statuses
        {
            get { return compositor.Statuses; }
            set { compositor.Statuses = value; }
        }

        internal long ActualHeight
        {
            get { return compositor.ListActualHeight; }
        }

        /// <summary>
        /// 選択されているステータス
        /// </summary>
        internal XStatus SelectedStatus
        {
            get { return compositor.SelectedStatus; }
        }

        internal void UpdateStatusWithSelectionPoint(IEnumerable<XStatus> status, ulong selected, long offset)
        {
            compositor.UpdateStatusWithSelectionPoint(status, selected, offset);
            InvokeUpdateTabDataValues();
        }

        internal event EventHandler UpdateTabDataValues;
        private void InvokeUpdateTabDataValues()
        {
            if (UpdateTabDataValues != null)
                UpdateTabDataValues.Invoke(this, new EventArgs());
        }

        internal long SelectedIndex
        {
            get { return compositor.SelectedIndex; }
            set { compositor.SelectedIndex = value; }
        }

        /// <summary>
        /// 選択を解除します。
        /// </summary>
        /// <param name="update">描画を更新するか</param>
        internal void Deselect(bool update)
        {
            compositor.Deselect(update);
        }

        /// <summary>
        /// Enumerate xstatus items in protected thread enumeration interruption
        /// </summary>
        /// <param name="callback">callback method</param>
        internal void DoEnumOperationWithProtectedThread(Action<XStatus> callback)
        {
            lock (compositor.statusesLocker)
            {
                foreach (var s in compositor.Statuses)
                {
                    callback(s);
                }
            }
        }

        internal event Action OnSelectedItemChanged;

        void compositor_OnStatusesChanged()
        {
            this.SafeOperate(() =>
                {
                    InvokeUpdateTabDataValues();
                    SetScrollMax();
                    SetScrollFromList();
                });
        }

        void compositor_OnVerticalPositionChanged()
        {
            this.SafeOperate(() =>
            {
                InvokeUpdateTabDataValues();
                if (scroller.Maximum != compositor.ListActualHeight)
                    SetScrollMax();
                SetScrollFromList();
            });
        }

        void compositor_OnSelectedItemChanged()
        {
            InvokeUpdateTabDataValues();
            if (OnSelectedItemChanged != null)
                OnSelectedItemChanged.Invoke();
        }

        private void ApplyScroll()
        {
            compositor.SetVerticalPosition(scroller.Value, false);
            InvokeUpdateTabDataValues();
        }

        private void SetScrollMax()
        {
            scroller.LargeChange = this.Height;
            int maximum = compositor.ListActualHeight - compositor.Height;
            if (maximum < 0) maximum = 0;
            scroller.Maximum = maximum;
            if ((scroller.Enabled || scroller.Maximum == 0) && scroller.Value > maximum)
            {
                scroller.Value = maximum;
                ApplyScroll();
            }
        }

        private void SetScrollFromList()
        {
            this.SafeOperate(() =>
            {
                if (scroller.Maximum < compositor.VerticalPosition)
                    compositor.VerticalPosition = scroller.Maximum;
                if (scroller.Minimum > compositor.VerticalPosition)
                    compositor.VerticalPosition = scroller.Minimum;
                try
                {
                    if (scroller.Value == compositor.VerticalPosition) return;
                    scroller.Value = (int)compositor.VerticalPosition;
                }
                catch (ArgumentOutOfRangeException) { }
                InvokeUpdateTabDataValues();
            });
        }

        internal int VerticalPosition
        {
            get { return scroller.Value; }
        }

        internal int VerticalPositionMax
        {
            get { return scroller.Maximum; }
        }

        internal void SetScrollTo(int value)
        {
            if (value > VerticalPositionMax)
                value = VerticalPositionMax;
            if (value < 0)
                value = 0;
            scroller.Value = value;
        }

        /// <summary>
        /// リストからスクロールバーの値を取って更新します。
        /// </summary>
        public void InitScrollbar()
        {
            compositor_OnVerticalPositionChanged();
        }

        internal int ScrollbarWidth
        {
            get { return scroller.Width; }
            set { scroller.Width = value; }
        }

        /// <summary>
        /// 表示画面を更新します。
        /// </summary>
        /// <param name="recomposite">リストの再構築をするか</param>
        public void SurfaceRedraw(bool recomposite)
        {
            compositor.Refresh(recomposite);
        }

        /// <summary>
        /// スクロール値
        /// </summary>
        internal int ScrollValue
        {
            get { return scroller.Value; }
            set
            {
                if (value > scroller.Maximum)
                    value = scroller.Maximum;
                if (value < 0)
                    value = 0;
                scroller.Value = value;
            }
        }

        private void PopWatcher()
        {
            int prevScrValue = -1;
            while (Core.IsAliveApp)
            {
                prevScrValue = ScrollValue;
                Thread.Sleep(120);
                if (Math.Abs(ScrollValue - prevScrValue) > Core.Config.Appearance.Renderer.Detail.NormalListHeight * 3)
                {
                    compositor.DrawTopDateTime = true;
                    while (Core.IsAliveApp)
                    {
                        prevScrValue = ScrollValue;
                        Thread.Sleep(700);
                        if (Math.Abs(ScrollValue - prevScrValue) < Core.Config.Appearance.Renderer.Detail.NormalListHeight)
                        {
                            compositor.DrawTopDateTime = false;
                            break;
                        }
                    }
                }
            }
        }

        private void scroller_Scroll(object sender, ScrollEventArgs e)
        {
            scroller_ValueChanged(sender, null);
        }

        private void scroller_ValueChanged(object sender, EventArgs e)
        {
            var act = new Action(() =>
            {
                this.SafeOperate(new Action(() =>
                {
                    //check clutch
                    bool sus = compositor.Refresher.IsSuspending;
                    try
                    {
                        if (!sus)
                            compositor.Refresher.Suspend();
                        if (scroller.Maximum != compositor.ListActualHeight)
                            SetScrollMax();
                        ApplyScroll();
                    }
                    finally
                    {
                        if (!sus)
                        {
                            compositor.Refresher.Restore(true, false);
                        }
                        InvokeUpdateTabDataValues();
                    }
                }));
            });
            act.BeginInvoke((iar) => ((Action)iar.AsyncState).EndInvoke(iar), act);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            SetScrollMax();
            base.OnSizeChanged(e);
        }

        internal void EnsureVisible()
        {
            compositor.EnsureVisible();
        }

        internal void EnsureVisibleTop()
        {
            SetScrollTo((int)(SelectedIndex * Core.Config.Appearance.Renderer.Detail.NormalListHeight));
        }

        internal bool SelectStatusByUID(ulong uid, bool p)
        {
            return compositor.SelectStatusByUID(uid, p);
        }

        internal void RefreshWeak(bool recomposite)
        {
            compositor.Refresher.RefreshSurface(recomposite, true);
        }

        internal void Refresh(bool recomposite)
        {
            compositor.Refresher.RefreshSurface(recomposite);
        }
    }
}
